package flow.service;

import com.alibaba.fastjson.JSONObject;
import flow.model.*;
import flow.util.FlowUtil;
import m.common.model.util.*;
import m.common.service.Service;
import m.system.db.TransactionManager;
import m.system.exception.MException;
import m.system.util.GenerateID;
import m.system.util.JSONMessage;
import m.system.util.StringUtil;
import manage.model.AdminLogin;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class FlowInstanceService extends Service {
	public JSONMessage getInstance(FlowInstance model) throws Exception {
		model=ModelQueryList.getModel(FlowInstance.class, model.getOid(), "*","business.*");
		JSONMessage result=new JSONMessage(0);
		result.push("model", model);
		result.push("list", ModelQueryList.getModelList(FlowInstanceSection.class, new String[] {"*","flowSection.*","user.*"}, null, 
			QueryCondition.eq("flowInstance.oid", model.getOid()),
			QueryOrder.asc("acceptDate"))
		);
		return result;
	}
	public String doStart(FlowBusiness business) throws Exception {
		start(FlowUtil.getFlowId(business.getBusiService(),business.getBusiParam()),business);
		return "启动成功";
	}

	/**
	 * 保存流程业务数据
	 */
	public String doSave(AdminLogin admin,FlowInstance model, JSONObject flowModel) throws Exception {
		TransactionManager tm=new TransactionManager();
		try {
			tm.begin(model.getBusiness().getOid());
			FlowUtil.getService(model.getBusiness().getBusiService()).saveFlowAfter(model, getNextInstanceSections(model.getOid()), flowModel);
			tm.commit();
		}catch (Exception e){
			tm.rollback();
			throw e;
		}
		return "保存成功";
	}
	/**
	 * 处理流程 
	 *  admin 当前处理人
	 *  model 流程实例
	 *  nextLink 下一环节链接
	 *  adminList 下一环节执行人
	 *  opinion 处理意见
	 * 
	 *  Exception
	 */
	public String doProcess(AdminLogin admin,FlowInstance model,FlowSectionLink nextLink,List<AdminLogin> adminList,String opinion, JSONObject flowModel) throws Exception {
		String msg="";
		model=ModelQueryList.getModel(model,"*","flowDefine.*","business.*");
		if(null==model) throw new MException(this.getClass(), "流程实例不存在");
		if(null==nextLink) {
			throw new MException(this.getClass(), "请选择下一步");
		}else if(StringUtil.isSpace(nextLink.getOid())) {
			if(adminList.size()!=1) throw new MException(this.getClass(), "请选择一个转发执行人");
			forward(model.getFlowDefine().getIdentity(), model.getBusiness(), admin, adminList.get(0),opinion);
			msg="转发成功";
		}else {
			nextLink=ModelQueryList.getModel(nextLink, "*","fromSection.*","toSection.*");
			if(null==nextLink) throw new MException(this.getClass(), "流程环节不存在");
			if("Y".equals(nextLink.getIsNext())) {
				next(model.getFlowDefine().getIdentity(),nextLink.getToParam(), model.getBusiness(), admin, adminList,opinion,flowModel);
				msg="操作成功";
			}else if("N".equals(nextLink.getIsNext())) {
				finish(model.getFlowDefine().getIdentity(),nextLink.getToParam(), model.getBusiness(), admin,opinion,flowModel);
				msg="已完成";
			}else if("B".equals(nextLink.getIsNext())) {
				back(model.getFlowDefine().getIdentity(), model.getBusiness(), admin,opinion,flowModel);
				msg="退回成功";
			}
		}
		return msg;
	}
	
	/**
	 * 开始流程
	 *  flowId 流程标识 
	 *  business 流程业务
	 *  user 开始流程人
	 *  Exception
	 */
	public void start(String flowId,FlowBusiness business) throws Exception {
		TransactionManager tm=new TransactionManager();
		try {
			tm.begin(business.getOid());
			FlowInstance ins=getInstance(flowId,business.getOid());
			if(null==ins) {//未启动才去启动
				ins = new FlowInstance();
				ins.setBusiness(business);
				ins.setFlowDefine(getService(FlowDefineService.class).getIssue(flowId));
				if (null == ins.getFlowDefine()) throw new MException(this.getClass(), "流程不存在");
				ins.setCurrentSection(ins.getFlowDefine().getStartSection());
				ins.setCurrentIndex(1);
				ins.setOid(GenerateID.generatePrimaryKey());
				ins.setCreateDate(new Date());
				ins.setDoneStatus("N");
				ModelUpdateUtil.insertModel(ins);
				getService(FlowBusinessService.class).updateCurrentSection(business.getOid(), ins.getCurrentSection(), ins.getCurrentIndex());
				FlowInstanceSection sec = new FlowInstanceSection();
				sec.setFlowInstance(ins);
				sec.setFlowSection(ins.getCurrentSection());
				sec.setFlowIndex(ins.getCurrentIndex());
				sec.setAcceptDate(new Date());
				sec.setDoneStatus("N");
				insertInsSec4Process(sec, business, "AU", new ArrayList<>());
			}
			tm.commit();
		}catch(Exception e) {
			tm.rollback();
			throw e;
		}
	}
	/**
	 * 流程转发
	 *  flowId 流程标识
	 *  business 流程业务
	 *  user 执行流程人
	 *  nextUser 下一步执行人
	 *  Exception
	 */
	public void forward(String flowId,FlowBusiness business,AdminLogin user,AdminLogin nextUser,String opinion) throws Exception {
		TransactionManager tm=new TransactionManager();
		try {
			tm.begin(business.getOid());
			FlowInstance ins=getInstance(flowId,business.getOid());
			if(null==ins) throw new MException(this.getClass(), "该流程实例不存在");
			if(!StringUtil.noSpace(ins.getCurrentSection().getForwardable()).equals("Y")) {
				throw new MException(this.getClass(), "该环节不支持转发");
			}
			if(user.getOid().equals(nextUser.getOid())) throw new MException(this.getClass(), "不能转发给自己");
			FlowInstanceSection sec=getInstanceSection(ins.getOid(), ins.getCurrentSection().getOid(), user.getOid());
			if(null==sec) throw new MException(this.getClass(), "实例环节不存在");
			if(null==sec.getUser()) throw new MException(this.getClass(), "部门的实例环节不支持转发");
			sec.setUser(user);
			sec.setNext("F");
			sec.setToDesc("转发");
			sec.setDoneDate(new Date());
			sec.setDoneStatus("Y");
			sec.setOpinion(opinion);
			ModelUpdateUtil.updateModel(sec, "next","toDesc","doneDate","doneStatus","user.oid","opinion");
			sec.setOid(GenerateID.generatePrimaryKey());
			sec.setUser(nextUser);
			sec.setNext(null);
			sec.setToDesc(null);
			sec.setDoneDate(null);
			sec.setDoneStatus("N");
			sec.setOpinion(null);
			sec.setAcceptDate(new Date());
			ModelUpdateUtil.insertModel(sec);
			FlowUtil.getService(business.getBusiService()).forwardFlowAfter(ins,getNextInstanceSections(ins.getOid()));
			tm.commit();
		}catch(Exception e) {
			tm.rollback();
			throw e;
		}
	}
	/**
	 * 执行流程下一步
	 *  flowId 流程标识
	 *  param 参数
	 *  business 流程业务
	 *  user 流程人
	 *  nextUsers 下一步流程人
	 *  下一步流程环节，通过后才返回
	 *  Exception
	 */
	public FlowInstance next(String flowId, String param, FlowBusiness business, AdminLogin user, List<AdminLogin> nextUsers, String opinion, JSONObject flowModel) throws Exception {
		TransactionManager tm=new TransactionManager();
		try {
			tm.begin(business.getOid());
			FlowInstance ins=getInstance(flowId,business.getOid());
			String p=toNext(ins,"Y",param,business,user,nextUsers,opinion);
			FlowUtil.getService(business.getBusiService()).processFlowAfter(p,ins,getNextInstanceSections(ins.getOid()),flowModel);
			getService(FlowBusinessService.class).checkDoneStatus(business.getOid());
			tm.commit();
			return ins;
		}catch(Exception e) {
			tm.rollback();
			throw e;
		}
	}
	/**
	 * 执行流程退回
	 *  flowId 流程标识
	 *  param 参数
	 *  business 流程业务
	 *  user 流程人
	 *  下一步流程环节，通过后才返回
	 *  Exception 
	 */
	public FlowInstance back(String flowId,FlowBusiness business,AdminLogin user,String opinion, JSONObject flowModel) throws Exception {
		TransactionManager tm=new TransactionManager();
		try {
			tm.begin(business.getOid());
			FlowInstance ins=getInstance(flowId,business.getOid());
			String p=toNext(ins,"B","",business,user, new ArrayList<>(),opinion);
			FlowUtil.getService(business.getBusiService()).processFlowAfter(p,ins,getNextInstanceSections(ins.getOid()),flowModel);
			getService(FlowBusinessService.class).checkDoneStatus(business.getOid());
			tm.commit();
			return ins;
		}catch(Exception e) {
			tm.rollback();
			throw e;
		}
	}
	/**
	 * 完成流程
	 *  flowId 流程标识
	 *  param 参数
	 *  business 流程业务
	 *  user 流程人
	 *  Exception
	 */
	public FlowInstance finish(String flowId,String param,FlowBusiness business,AdminLogin user,String opinion, JSONObject flowModel) throws Exception {
		TransactionManager tm=new TransactionManager();
		try {
			tm.begin(business.getOid());
			FlowInstance ins=getInstance(flowId,business.getOid());
			String p=toNext(ins,"N",param,business,user, new ArrayList<>(),opinion);
			FlowUtil.getService(business.getBusiService()).processFlowAfter(p,ins,getNextInstanceSections(ins.getOid()),flowModel);
			tm.commit();
			return ins;
		}catch(Exception e) {
			tm.rollback();
			throw e;
		}
	}
	private String toNext(FlowInstance ins,String isNext,String param,FlowBusiness business,AdminLogin user,List<AdminLogin> nextUsers,String opinion) throws Exception {
		String p="";
		FlowSection result;
		if(null==ins) throw new MException(this.getClass(), "该流程实例不存在");
		getService(FlowBusinessService.class).clearNextParams(business.getOid());
		ins.setBusiness(business);
		FlowInstanceSection sec=getInstanceSection(ins.getOid(), ins.getCurrentSection().getOid(), user.getOid());
		if(null==sec) throw new MException(this.getClass(), "当前登录人的实例环节不存在");
		sec.setNext(isNext);
		sec.setParam(param);
		sec.setDoneDate(new Date());
		sec.setDoneStatus("Y");
		sec.setUser(user);
		sec.setOpinion(opinion);
		ModelUpdateUtil.updateModel(sec, "next","param","doneDate","doneStatus","user.oid","opinion");
		if(!"Y".equals(ins.getCurrentSection().getCountersign())) {//非会签，设置该环节其他待办跳过
			setPassInstanceSection(ins.getOid(), ins.getCurrentSection().getOid());
		}
		FlowSectionLink link=getService(FlowSectionService.class).getLink(ins.getCurrentSection().getFlowDefine().getOid(),ins.getCurrentSection().getIdentity(), isNext, param);
		sec.setToDesc(link.getToDesc());
		ModelUpdateUtil.updateModel(sec, "toDesc");
		if(isOverInstanceSection(ins.getOid(), ins.getCurrentSection().getOid())) {//当前环节代表是否都完成
			ins.setCurrentIndex(ins.getCurrentIndex()+1);
			if(instanceSectionResult(ins.getOid(), ins.getCurrentSection().getOid(), ins.getCurrentIndex()-1)) {//统计结果是否一致
				//按照当前环节走下一步
				p=link.getToParam();
				if(!"N".equals(link.getIsNext())) {//下一步 or 退回
					if(isNext.equals("Y")) {
						toNextSection(ins,link, nextUsers);
					}else {
						toBackSection(ins,link);
					}
					result=link.getToSection();
				}else {//结束
					ins.setDoneDate(new Date());
					ins.setDoneStatus("Y");
					ModelUpdateUtil.updateModel(ins, "doneDate","doneStatus");
					result=new FlowSection();
				}
			}else {//如果是会签，结果不一致，走退回环节
				link=getService(FlowSectionService.class).getLink(ins.getCurrentSection().getFlowDefine().getOid(),ins.getCurrentSection().getIdentity(), "B", "");
				//走当前环节的退回
				result=toBackSection(ins,link);
			}
			if(null==result) throw new MException(this.getClass(), "未找到下一环节");
			ins.setCurrentSection(result);
			ModelUpdateUtil.updateModel(ins, "currentSection.oid","currentIndex");
			getService(FlowBusinessService.class).updateCurrentSection(business.getOid(), ins.getCurrentSection(), ins.getCurrentIndex());
		}
		return p;
	}
	private FlowSection toBackSection(FlowInstance ins,FlowSectionLink link) throws SQLException, MException {
		List<FlowInstanceSection> list=ModelQueryList.getModelList(FlowInstanceSection.class,
			new String[] {"flowInstance.oid","flowSection.oid","user.oid"},null,
			QueryCondition.and(
				QueryCondition.eq("flowInstance.oid", ins.getOid()),
				QueryCondition.eq("flowSection.oid", link.getToSection().getOid()),
				QueryCondition.not(QueryCondition.eq("next", "F")),
				QueryCondition.in("flowIndex",
					new QueryParameter(
						"select max(flow_index) idx from of_flow_ins_section where flow_instance_oid=? and flow_section_oid=?",
						new Object[] {ins.getOid(),link.getToSection().getOid()}
					)
				))
		);
		Date d=new Date();
		if(!list.isEmpty()) {
			for(FlowInstanceSection fs : list) {
				fs.setOid(GenerateID.generatePrimaryKey());
				fs.setFlowIndex(ins.getCurrentIndex());
				fs.setAcceptDate(d);
				fs.setDoneStatus("N");
			}
		}else {
			FlowInstanceSection fs=new FlowInstanceSection();
			fs.setOid(GenerateID.generatePrimaryKey());
			fs.setFlowInstance(ins);
			fs.setFlowSection(link.getToSection());
			fs.setUser(ins.getBusiness().getApplyUser());
			fs.setFlowIndex(ins.getCurrentIndex());
			fs.setAcceptDate(d);
			fs.setDoneStatus("N");
			list.add(fs);
		}
		ModelUpdateUtil.insertModels(list.toArray(new FlowInstanceSection[] {}));
		return link.getToSection();
	}
	private void toNextSection(FlowInstance ins,FlowSectionLink link,List<AdminLogin> nextUsers) throws MException {
		FlowInstanceSection fis=new FlowInstanceSection();
		fis.setFlowInstance(ins);
		fis.setFlowSection(link.getToSection());
		fis.setFlowIndex(ins.getCurrentIndex());
		fis.setAcceptDate(new Date());
		fis.setDoneStatus("N");
		fis.setToOrgOption(link.getToOrgOption());
		fis.setToOrg(link.getToOrg());
		fis.setToRole(link.getToRole());
		insertInsSec4Process(fis, ins.getBusiness(), link.getToOption(), nextUsers);
	}
	/**
	 * 获取未完成的流程实例
	 *  flowId 流程标识
	 *  businessOid 业务oid
	 * 
	 *  SQLException
	 *  MException
	 */
	private FlowInstance getInstance(String flowId,String businessOid) throws SQLException, MException {
		return ModelQueryList.getModel(FlowInstance.class, new String[] {"*","flowDefine.*","currentSection.*"},
			QueryCondition.and(
				QueryCondition.eq("flowDefine.identity", flowId),
				QueryCondition.eq("business.oid", businessOid),
				QueryCondition.eq("doneStatus", "N"))
		);
	}
	/**
	 * 获取未完成的流程实例环节
	 *  insOid 实例oid
	 *  secOid 环节oid
	 *  userOid 环节执行人
	 * 
	 *  SQLException
	 *  MException
	 */
	private FlowInstanceSection getInstanceSection(String insOid,String secOid,String userOid) throws SQLException, MException {
		return getInstanceSection(insOid, secOid, userOid,"N");
	}
	/**
	 * 获取流程实例环节 
	 *  insOid
	 *  secOid
	 *  userOid
	 *  doneStatus
	 * 
	 *  SQLException
	 *  MException
	 */
	public FlowInstanceSection getInstanceSection(String insOid,String secOid,String userOid,String doneStatus) throws SQLException, MException {
		List<QueryCondition> cons= new ArrayList<>();
		cons.add(QueryCondition.eq("flowInstance.oid",insOid));
		cons.add(QueryCondition.eq("flowSection.oid",secOid));
		cons.add(QueryCondition.eq("user.oid", userOid));
		if(!StringUtil.isSpace(doneStatus)) {
			cons.add(QueryCondition.eq("doneStatus", doneStatus));
		}
		return ModelQueryList.getModel(FlowInstanceSection.class, new String[] {"*","flowSection.*","flowInstance.*"},
			QueryCondition.and(cons.toArray(new QueryCondition[] {})),
			QueryOrder.asc("doneStatus"));
	}

	/**
	 *  获取下一步流程实例环节
	 *  insOid
	 *  SQLException
	 *  MException
	 */
	public List<FlowInstanceSection> getNextInstanceSections(String insOid) throws SQLException, MException {
		List<QueryCondition> cons= new ArrayList<>();
		cons.add(QueryCondition.eq("flowInstance.oid",insOid));
		cons.add(QueryCondition.eq("doneStatus", "N"));
		return ModelQueryList.getModelList(FlowInstanceSection.class, new String[] {"*","user.*","flowSection.*","flowInstance.*"},null,
				QueryCondition.and(cons.toArray(new QueryCondition[] {})),
				QueryOrder.asc("doneStatus"));
	}
	/**
	 * 该实例环节是否都完成 
	 *  insOid
	 *  secOid
	 * 
	 *  SQLException
	 *  MException
	 */
	private boolean isOverInstanceSection(String insOid,String secOid) throws SQLException, MException {
		return ModelQueryList.getModelList(FlowInstanceSection.class, new String[] {"*"},null,
			QueryCondition.and(
				QueryCondition.eq("flowInstance.oid",insOid),
				QueryCondition.eq("flowSection.oid",secOid),
				QueryCondition.eq("doneStatus", "N"))
		).size()==0;
	}
	/**
	 * 设置该实例环节其他待办跳过
	 *  insOid
	 *  secOid
	 *  SQLException
	 *  MException
	 */
	private void setPassInstanceSection(String insOid,String secOid) throws SQLException, MException {
		List<FlowInstanceSection> list=ModelQueryList.getModelList(FlowInstanceSection.class, new String[] {"oid"},null,
			QueryCondition.and(
				QueryCondition.eq("flowInstance.oid",insOid),
				QueryCondition.eq("flowSection.oid",secOid),
				QueryCondition.eq("doneStatus", "N"))
		);
		for(FlowInstanceSection sec : list) {
			sec.setNext("P");
			sec.setParam("");
			sec.setToDesc("");
			sec.setDoneDate(new Date());
			sec.setDoneStatus("Y");
		}
		ModelUpdateUtil.updateModels(list.toArray(new FlowInstanceSection[] {}), "next","param","doneDate","doneStatus");
	}
	/**
	 * 该实例环节结果 true表示走下一步
	 *  insOid
	 *  secOid
	 * 
	 *  SQLException
	 *  MException
	 */
	private boolean instanceSectionResult(String insOid,String secOid,Integer flowIndex) throws SQLException, MException {
		List<FlowInstanceSection> list=ModelQueryList.getModelList(
			FlowInstanceSection.class, new String[] {"next","param"},null,
			QueryCondition.and(
				QueryCondition.eq("flowInstance.oid",insOid),
				QueryCondition.eq("flowSection.oid",secOid),
				QueryCondition.eq("flowIndex",flowIndex),
				QueryCondition.eq("doneStatus", "Y"),
				QueryCondition.not(QueryCondition.eq("next", "F")),//转发
				QueryCondition.not(QueryCondition.eq("next", "P"))//跳过
			)
		);
		for(int i=1;i<list.size();i++) {
			if(list.get(i).getNext().equals(list.get(i-1).getNext())
					&&list.get(i).getParam().equals(list.get(i-1).getParam())) {
			}else return false;
		}
		return true;
	}
	/**
	 * 插入实例环节
	 *  insSec 只剩user未设置
	 *  business 
	 *  option 下一步参数 AU申请人,MU多选用户,OU单选用户
	 *  userList
	 *  MException
	 */
	private void insertInsSec4Process(FlowInstanceSection insSec,FlowBusiness business,String option,List<AdminLogin> userList) throws MException {
		if(StringUtil.noSpace(option).length()!=2) {
			throw new MException(this.getClass(), "选项参数错误");
		}else if(option.lastIndexOf("U")==1||option.lastIndexOf("O")==1) {
			if(option.indexOf("A")==0) {//发起
				insSec.setOid(GenerateID.generatePrimaryKey());
				if(option.lastIndexOf("U")==1) {
					insSec.setUser(business.getApplyUser());
				}
				ModelUpdateUtil.insertModel(insSec);
				return;
			}else if(option.indexOf("M")==0) {//多选
				if(option.lastIndexOf("U")==1) {
					if(userList.isEmpty()) throw new MException(this.getClass(), "请选择处理人");
					for(AdminLogin user : userList) {
						insSec.setOid(GenerateID.generatePrimaryKey());
						insSec.setUser(user);
						ModelUpdateUtil.insertModel(insSec);
					}
					return;
				}
			}else if(option.indexOf("O")==0) {//单选
				insSec.setOid(GenerateID.generatePrimaryKey());
				if(option.lastIndexOf("U")==1) {
					if(userList.size()!=1) throw new MException(this.getClass(), "请选择一个处理人");
					insSec.setUser(userList.get(0));
				}
				ModelUpdateUtil.insertModel(insSec);
				return;
			}
		} 
		throw new MException(this.getClass(),"选项参数错误");
	}
}
